// ------------- 数组类型的递归复用 -------------

{
  type arr = [1, 2, 3, 4, 5];

  // 翻转

  type ReverseArr<T extends unknown[]> = T extends [infer A, infer B, infer C, infer D, infer E]
    ? [E, D, C, B, A]
    : never;

  type res = ReverseArr<arr>;
}

// ------------- 翻转数组 -----------------

{
  // 翻转时数组长度不确定，就需要递归

  type arr = [1, 2, 3, 4, 5];

  type ReverseArr<Arr extends unknown[]> = Arr extends [infer A, ...infer Rest]
    ? [...ReverseArr<Rest>, A]
    : Arr;

  type res = ReverseArr<arr>;

  /*
    数量不确定，条件反射的就要想到递归。
    我们每次只处理一个类型，剩下的递归做，直到满足结束条件。
    类型参数 Arr 为待处理的数组类型，元素类型不确定，也就是 unknown。
    每次只处理一个元素的提取，放到 infer 声明的局部变量 First 里，剩下的放到 Rest 里。
    用 First 作为最后一个元素构造新数组，其余元素递归的取。
    结束条件就是取完所有的元素，也就是不再满足模式匹配的条件，这时候就返回 Arr。
  */
}

// ------------- 判断类型是否相同 -----------------

{
  // 实现 数组的 includes方法
  // 从长度不固定的数组中查找某个元素，数量不确定，这时候就应该想到递归。

  type IsEqual<A, B> = A extends B ? true : false;

  type res = IsEqual<true, boolean>;

  // type res = true

  type res2 = IsEqual<boolean, true>;
  // type res2 = boolean

  // res2 的返回值为 boolean 说明IsEqual并没有完全遵循返回值

  type res3 = IsEqual<1, number>; // type res3 = true
  type res4 = IsEqual<number, 1>; // type res4 = false
}

{
  // isEqual 改进：相等的判断就是 A 是 B 的子类型并且 B 也是 A 的子类型，。

  type IsEqual<A, B> = (A extends B ? true : false) & (B extends A ? true : false);

  type res = IsEqual<true, boolean>; // type res = true
  type res2 = IsEqual<boolean, true>; // type res2 = true

  type res3 = IsEqual<1, number>; // type res3 = nerve
  type res4 = IsEqual<number, 1>; // type res4 = never
}

// ----------------- 实现类似数组中的includes方法 -----------------

{
  type IsEqual<A, B> = (A extends B ? true : false) & (B extends A ? true : false);

  type Includes<Arr extends unknown[], FindItem> = Arr extends [infer First, ...infer Rest]
    ? IsEqual<First, FindItem> extends true
      ? true
      : Includes<Rest, FindItem>
    : false;

  type res = Includes<[1, 2, 3, 4], 1>; // true
  type res2 = Includes<[1, 2, 3, 4], '3'>; // false

  type res3 = Includes<[{ a: number }], { a: number }>; // true
  type res4 = Includes<[{ a: number }], { a: string }>; // false
  type res5 = Includes<[{}], {}>; // true

  type res6 = Includes<[1, 2, true, 3], true>; // true
  type res7 = Includes<[1, 2, true, 3], 'true'>; // false
  type res8 = Includes<[1, 2, false, 3], false>; // true
  type res9 = Includes<[1, 2, false, 3], boolean>; // true
  type res10 = Includes<[1, 2, true, 3], boolean>; // true
  type res11 = Includes<[1, 2, boolean, 3], true>; // true
  type res12 = Includes<[1, 2, boolean, 3], false>; // true
  type res13 = Includes<[1, 2, number, 3], 4>; // true
  type res14 = Includes<[1, 2, 4, 3], number>; // true
  type res15 = Includes<[{ readonly a: 'A' }], { a: 'A' }>; // true
  type res16 = Includes<[{ a?: 'A' }], { a: 'A' }>; // true
  type res17 = Includes<[{ a: 'A' }], { a: string }>; // true
  type res18 = Includes<[{ a: string }], { a: 'A' }>; // true

  /*
    既然递归可以做循环用，那么像查找元素这种自然也就可以实现。

    比如查找 [1, 2, 3, 4, 5] 中是否存在 4，是就返回 true，否则返回 false。

    从长度不固定的数组中查找某个元素，数量不确定，这时候就应该想到递归。

    -----------

    类型参数 Arr 是待查找的数组类型，元素类型任意，也就是 unknown。FindItem 待查找的元素类型。

    每次提取一个元素到 infer 声明的局部变量 First 中，剩余的放到局部变量 Rest。

    判断 First 是否是要查找的元素，也就是和 FindItem 相等，是的话就返回 true，否则继续递归判断下一个元素。

    直到结束条件也就是提取不出下一个元素，这时返回 false。

    相等的判断就是 A 是 B 的子类型并且 B 也是 A 的子类型，。

    这样就完成了不确定长度的数组中的元素查找，用递归实现了循环。
  */
}

{
  // includes方法

  type IsEqual<T, K> = (T extends K ? true : false) & (K extends T ? true : false);

  type Includes<T extends unknown[], K> = T extends [infer X, ...infer Rest]
    ? IsEqual<X, K> extends true
      ? true
      : Includes<Rest, K>
    : false;

  type res = Includes<[1, 2, 3, 4], 1>; // true
  type res2 = Includes<[1, 2, 3, 4], '3'>; // false
}

// ---------------- 实现删除数组项 ---------------------

{
  // RemoveItem

  type IsEqual<T, K> = (T extends K ? true : false) & (K extends T ? true : false);

  type RemoveItem<Arr extends unknown[], Item, Result extends unknown[] = []> = Arr extends [
    infer First,
    ...infer Rest
  ]
    ? IsEqual<First, Item> extends true
      ? RemoveItem<Rest, Item, Result>
      : RemoveItem<Rest, Item, [...Result, First]>
    : Result;

  type res = RemoveItem<[1, 2, 3, 4], 2>;

  /*
    类型参数 Arr 是待处理的数组，元素类型任意，也就是 unknown[]。类型参数 Item 为待查找的元素类型。类型参数 Result 是构造出的新数组，默认值是 []。

    通过模式匹配提取数组中的一个元素的类型，如果是 Item 类型的话就删除，也就是不放入构造的新数组，直接返回之前的 Result。

    否则放入构造的新数组，也就是再构造一个新的数组 [...Result, First]。

    直到模式匹配不再满足，也就是处理完了所有的元素，返回这时候的 Result。

    这样我们就完成了不确定元素个数的数组的某个元素的删除：
  */
}

{
  // RemoveItem 这样也可以

  type IsEqual<T, K> = (T extends K ? true : false) & (K extends T ? true : false);

  type RemoveItem<Arr extends unknown[], Item> = Arr extends [infer A, ...infer Rest]
    ? IsEqual<A, Item> extends true
      ? RemoveItem<Rest, Item>
      : [A, ...RemoveItem<Rest, Item>]
    : Arr;

  type res = RemoveItem<[1, 2, 3, 4], 2>;
}

{
  // BuildArray 生成指定长度的array

  type BuildArray<
    Length extends number,
    Ele = unknown,
    Arr extends unknown[] = [],
  > = Arr['length'] extends Length ? Arr : BuildArray<Length, Ele, [...Arr, Ele]>;

  type res = BuildArray<5>;

  /*
  类型参数 Length 为数组长度，约束为 number。类型参数 Ele 为元素类型，默认值为 unknown。类型参数 Arr 为构造出的数组，默认值是 []。

  每次判断下 Arr 的长度是否到了 Length，是的话就返回 Arr，否则在 Arr 上加一个元素，然后递归构造。
  */
}
